﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Web.Script.Serialization;

namespace CSharp.Infrastructure
{
    /// <summary>
    /// 文件系统数据库
    /// 主键Attribute [Key]，支持主键比较
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class FileDbHelper<T> where T : class
    {
        private static JavaScriptSerializer js = new JavaScriptSerializer() { MaxJsonLength = int.MaxValue };

        private bool is_save_file = false;
        private bool is_save_bak_file = false;

        // 是否可以删除备份
        private bool is_ok = false;

        private List<T> data_list = new List<T>();
        private string savePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "db", typeof(T).Name + ".txt");

        public FileDbHelper(bool is_save_file = false, bool is_save_bak_file = true)
        {
            if (is_save_file)
            {
                string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "db");
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }

                string savePath = Path.Combine(dir, typeof(T).Name + ".txt");
                if (File.Exists(savePath))
                {
                    try
                    {
                        string[] lines = File.ReadAllLines(savePath, Encoding.UTF8);
                        foreach (var line in lines)
                        {
                            if (!string.IsNullOrEmpty(line))
                            {
                                data_list.Add(js.Deserialize<T>(line));
                            }
                        }

                        is_ok = true;
                    }
                    catch
                    {
                        throw new Exception("数据异常，需要使用bak恢复");
                    }
                }
            }
        }

        public void Add(T model)
        {
            lock (data_list)
            {
                data_list.Add(model);
                SaveFile();
            }
        }

        public void AddList(List<T> list)
        {
            lock (data_list)
            {
                data_list.AddRange(list);
                SaveFile();
            }
        }

        public void AddOrUpdate(T model, Predicate<T> match)
        {
            lock (data_list)
            {
                var item = data_list.Find(match);
                if (item == null)
                {
                    data_list.Add(item);
                }
                else
                {
                    item = model;
                }

                SaveFile();
            }
        }

        public T QueryInfo(Predicate<T> match)
        {
            lock (data_list)
            {
                return data_list.Find(match);
            }
        }

        public List<T> QueryList(int page, int pageSize, Expression<Func<T, bool>> predicate)
        {
            lock (data_list)
            {
                return data_list.AsQueryable().Where(predicate).Skip(pageSize * (page - 1)).Take(pageSize).ToList();
            }
        }

        private void SaveFile()
        {
            if (is_save_file)
            {
                StringBuilder content = new StringBuilder();
                foreach (var item in data_list)
                {
                    content.AppendLine(js.Serialize(item));
                }

                if (is_save_bak_file && is_ok)
                {
                    if (File.Exists(savePath))
                    {
                        File.Copy(savePath, savePath + ".bak");
                    }
                }

                File.WriteAllText(savePath, content.ToString(), new UTF8Encoding(false));
            }
        }
    }
}
